use std::{
	ffi::{self, CStr},
	marker::PhantomData,
	ptr,
};

use sys::{JNINativeMethod, JNI_OK};

use crate::{
	AsJValue, AsJValueArray, FromJValue, JArray, JArrayObject, JArrayPrim, JArrayPrimMaker, JBool, JByte, JChar, JClassID, JDouble,
	JFieldID, JFieldStaticID, JFloat, JInt, JLocal, JLong, JMethodID, JMethodStaticID, JNativeMethod, JObject, JPlainMarker, JRc, JRefType,
	JReturnMaker, JReturnType, JShort, JSize, JString, JThrowable, JVm, JWeak, JTRUE,
};

pub struct ReleaseMode(JInt);
impl ReleaseMode {
	pub const COMMIT_FREE: ReleaseMode = ReleaseMode(0);
	pub const COMMIT: ReleaseMode = ReleaseMode(sys::JNI_COMMIT);
	pub const ABORT: ReleaseMode = ReleaseMode(sys::JNI_ABORT);
}

#[repr(transparent)]
#[derive(Debug)]
pub struct JEnv {
	pub(crate) inner: sys::JNIEnv,
}
impl JEnv {
	pub fn as_sys(&self) -> *mut sys::JNIEnv {
		self as *const Self as *const ffi::c_void as *const sys::JNIEnv as _
	}
	pub(crate) fn from_sys(env: *mut ffi::c_void) -> Option<&'static JEnv> {
		if env.is_null() {
			None
		} else {
			Some(unsafe { &*(env as *const JEnv) })
		}
	}
	fn vtable(&self) -> &sys::JNINativeInterface_ {
		unsafe { &*self.inner }
	}
	pub fn vm(&self) -> Option<JVm> {
		let mut ptr = ptr::null_mut();
		let fun = self.vtable().GetJavaVM?;
		if JNI_OK == unsafe { fun(self.as_sys(), &mut ptr) } {
			return Some(JVm { inner: ptr });
		}
		None
	}
	pub fn version(&self) -> JInt {
		if let Some(fun) = self.vtable().GetVersion {
			unsafe { fun(self.as_sys()) }
		} else {
			sys::JNI_VERSION_1_1
		}
	}
	pub fn env(env: Option<&JEnv>) -> Option<&JEnv> {
		Some(env.unwrap_or(JVm::singleton(None)?.env()?))
	}

	pub fn exception_check(&self) -> Option<bool> {
		let fun = self.vtable().ExceptionCheck?;
		let ret = unsafe { fun(self.as_sys()) };
		Some(ret == JTRUE)
	}
	pub fn exception_describe(&self) -> Option<()> {
		let fun = self.vtable().ExceptionDescribe?;
		let _ = unsafe { fun(self.as_sys()) };
		Some(())
	}
	pub fn exception_clear(&self) -> Option<()> {
		let fun = self.vtable().ExceptionClear?;
		let _ = unsafe { fun(self.as_sys()) };
		Some(())
	}
	pub fn exception_occurred(&self) -> Option<&JThrowable> {
		let fun = self.vtable().ExceptionOccurred?;
		let ret = unsafe { fun(self.as_sys()) };
		JThrowable::from_sys(ret)
	}

	pub fn find_class(&self, name: &[u8]) -> Option<JClassID> {
		let fun = self.vtable().FindClass?;
		let cls = unsafe { fun(self.as_sys(), name.as_ptr() as *const ffi::c_char) };
		let fun = self.vtable().NewGlobalRef?;
		let cls = unsafe { fun(self.as_sys(), cls) };
		JClassID::from_sys(cls)
	}
	pub(crate) fn super_class(&self, this: JClassID) -> Option<JClassID> {
		let fun = self.vtable().GetSuperclass?;
		let cls = unsafe { fun(self.as_sys(), this.as_sys()) };
		let fun = self.vtable().NewGlobalRef?;
		let cls = unsafe { fun(self.as_sys(), cls) };
		JClassID::from_sys(cls)
	}
	pub(crate) fn is_assignable(&self, from: JClassID, to: JClassID) -> Option<bool> {
		let fun = self.vtable().IsAssignableFrom?;
		if sys::JNI_TRUE == unsafe { fun(self.as_sys(), from.as_sys(), to.as_sys()) } {
			Some(true)
		} else {
			Some(false)
		}
	}

	pub(crate) fn get_string(&self, string: &JString) -> Option<String> {
		let fun = self.vtable().GetStringUTFChars?;
		let del = self.vtable().ReleaseStringUTFChars?;
		let jmutf8 = unsafe { fun(self.as_sys(), string.as_sys(), ptr::null_mut()) };
		if !jmutf8.is_null() {
			let bytes = unsafe { CStr::from_ptr(jmutf8) }.to_bytes();
			let output = cesu8::from_java_cesu8(bytes).ok()?.into_owned();
			unsafe { del(self.as_sys(), string.as_sys(), jmutf8) };
			Some(output)
		} else {
			None
		}
	}
	pub fn new_string(&self, value: impl AsRef<str>) -> Option<&JString> {
		let fun = self.vtable().NewStringUTF?;
		let java_str = cesu8::to_java_cesu8(value.as_ref());
		let cstring = std::ffi::CString::new(java_str).ok()?;
		let ret = unsafe { fun(self.as_sys(), cstring.as_ptr() as _) };
		JString::from_sys(ret)
	}

	pub(crate) fn array_len(&self, array: &JArray) -> Option<JInt> {
		let fun = self.vtable().GetArrayLength?;
		let ret = unsafe { fun(self.as_sys(), array.as_sys()) };
		Some(ret)
	}

	pub(crate) fn new_array_object<T: AsRef<JObject>>(&self, size: JSize, class: JClassID, default: &JObject) -> Option<&JArrayObject<T>> {
		let fun = self.vtable().NewObjectArray?;
		let ret = unsafe { fun(self.as_sys(), size, class.as_sys(), default.as_sys()) };
		if !ret.is_null() {
			Some(unsafe { &*(ret as *mut JArrayObject<T>) })
		} else {
			None
		}
	}
	pub(crate) fn array_object_get<T: AsRef<JObject>>(&self, array: &JArrayObject<T>, index: JSize) -> Option<&T> {
		let fun = self.vtable().GetObjectArrayElement?;
		let ret = unsafe { fun(self.as_sys(), array.as_sys(), index) };
		if !ret.is_null() {
			Some(unsafe { &*(ret as *mut T) })
		} else {
			None
		}
	}
	pub(crate) fn array_object_set<T: AsRef<JObject>>(&self, array: &JArrayObject<T>, index: JSize, value: &T) -> Option<()> {
		let fun = self.vtable().SetObjectArrayElement?;
		let ret = unsafe { fun(self.as_sys(), array.as_sys(), index, value.as_ref().as_sys()) };
		Some(ret)
	}

	pub(crate) fn new_array<T: JArrayPrimMaker>(&self, size: JSize) -> Option<&JArrayPrim<T>> {
		use crate::JArrayPrimType::*;
		let ret = match T::TYPE {
			Bool => {
				let fun = self.vtable().NewBooleanArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Byte => {
				let fun = self.vtable().NewByteArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Char => {
				let fun = self.vtable().NewCharArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Short => {
				let fun = self.vtable().NewShortArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Int => {
				let fun = self.vtable().NewIntArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Long => {
				let fun = self.vtable().NewLongArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Float => {
				let fun = self.vtable().NewFloatArray?;
				unsafe { fun(self.as_sys(), size) }
			}
			Double => {
				let fun = self.vtable().NewDoubleArray?;
				unsafe { fun(self.as_sys(), size) }
			}
		};
		if !ret.is_null() {
			Some(unsafe { &*(ret as *mut JArrayPrim<T>) })
		} else {
			None
		}
	}
	pub(crate) fn lock_array<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, is_copy: &mut JBool) -> Option<&mut [T]> {
		let object: &JObject = array.as_ref();
		let len = self.array_len(array.as_ref())?.try_into().ok()?;
		use crate::JArrayPrimType::*;
		let ret = match T::TYPE {
			Bool => {
				let fun = self.vtable().GetBooleanArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Byte => {
				let fun = self.vtable().GetByteArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Char => {
				let fun = self.vtable().GetCharArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Short => {
				let fun = self.vtable().GetShortArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Int => {
				let fun = self.vtable().GetIntArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Long => {
				let fun = self.vtable().GetLongArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Float => {
				let fun = self.vtable().GetFloatArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
			Double => {
				let fun = self.vtable().GetDoubleArrayElements?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) };
				ret as *mut ffi::c_void
			}
		} as *mut T;
		if !ret.is_null() {
			let ret = unsafe { core::slice::from_raw_parts_mut(ret, len) };
			Some(ret)
		} else {
			None
		}
	}
	pub(crate) fn unlock_array<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, data: &mut [T], mode: ReleaseMode) -> Option<()> {
		let object: &JObject = array.as_ref();
		let ptr = data.as_mut_ptr() as *mut ffi::c_void;
		let ret = match T::TYPE {
			crate::JArrayPrimType::Bool => {
				let fun = self.vtable().ReleaseBooleanArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Byte => {
				let fun = self.vtable().ReleaseByteArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Char => {
				let fun = self.vtable().ReleaseCharArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Short => {
				let fun = self.vtable().ReleaseShortArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Int => {
				let fun = self.vtable().ReleaseIntArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Long => {
				let fun = self.vtable().ReleaseLongArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Float => {
				let fun = self.vtable().ReleaseFloatArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
			crate::JArrayPrimType::Double => {
				let fun = self.vtable().ReleaseDoubleArrayElements?;
				unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) }
			}
		};
		Some(ret)
	}
	pub(crate) fn replace_array<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, at: JInt, data: &[T]) -> Option<()> {
		let object: &JObject = array.as_ref();
		let len = data.len() as JInt;
		let ptr = data.as_ptr() as *const ffi::c_void;
		let ret = match T::TYPE {
			crate::JArrayPrimType::Bool => {
				let fun = self.vtable().SetBooleanArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Byte => {
				let fun = self.vtable().SetByteArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Char => {
				let fun = self.vtable().SetCharArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Short => {
				let fun = self.vtable().SetShortArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Int => {
				let fun = self.vtable().SetIntArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Long => {
				let fun = self.vtable().SetLongArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Float => {
				let fun = self.vtable().SetFloatArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Double => {
				let fun = self.vtable().SetDoubleArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
		};
		Some(ret)
	}
	pub(crate) fn copy_array<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, at: JInt, data: &mut [T]) -> Option<()> {
		let object: &JObject = array.as_ref();
		let len = data.len() as JInt;
		let ptr = data.as_mut_ptr() as *mut ffi::c_void;
		let ret = match T::TYPE {
			crate::JArrayPrimType::Bool => {
				let fun = self.vtable().GetBooleanArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Byte => {
				let fun = self.vtable().GetByteArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Char => {
				let fun = self.vtable().GetCharArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Short => {
				let fun = self.vtable().GetShortArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Int => {
				let fun = self.vtable().GetIntArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Long => {
				let fun = self.vtable().GetLongArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Float => {
				let fun = self.vtable().GetFloatArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
			crate::JArrayPrimType::Double => {
				let fun = self.vtable().GetDoubleArrayRegion?;
				unsafe { fun(self.as_sys(), object.as_sys(), at, len, ptr as _) }
			}
		};
		Some(ret)
	}
	pub(crate) fn lock_array_critical<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, is_copy: &mut JBool) -> Option<&mut [T]> {
		let object: &JObject = array.as_ref();
		let len = self.array_len(array.as_ref())?.try_into().ok()?;
		let fun = self.vtable().GetPrimitiveArrayCritical?;
		let ret = unsafe { fun(self.as_sys(), object.as_sys(), is_copy as *mut JBool) } as *mut T;
		if !ret.is_null() {
			let ret = unsafe { core::slice::from_raw_parts_mut(ret, len) };
			Some(ret)
		} else {
			None
		}
	}
	pub(crate) fn unlock_array_critical<T: JArrayPrimMaker>(&self, array: &JArrayPrim<T>, data: &mut [T], mode: ReleaseMode) -> Option<()> {
		let object: &JObject = array.as_ref();
		let ptr = data.as_mut_ptr() as *mut ffi::c_void;
		let fun = self.vtable().ReleasePrimitiveArrayCritical?;
		let ret = unsafe { fun(self.as_sys(), object.as_sys(), ptr as _, mode.0) };
		Some(ret)
	}

	/// Constructs a new Java object.
	/// The method ID indicates which constructor method to invoke.
	/// This ID must be obtained by calling GetMethodID() with <init> as the method name and void (V) as the return type.
	/// The clazz argument must not refer to an array class.(what is array class?)
	pub(crate) fn new_object<const N: usize, T: JPlainMarker>(&self, method: JMethodID, args: impl AsJValueArray<N>) -> Option<&T> {
		let fun = self.vtable().NewObjectA?;
		#[allow(dangling_pointers_from_temporaries)]
		let args = args.as_jvalue_array().as_ptr();
		let class = T::class(Some(self))?;
		let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
		if !ret.is_null() {
			let obj = unsafe { &*(ret as *mut JObject) };
			unsafe { obj.cast_unchecked() }
		} else {
			None
		}
	}
	pub(crate) fn object_class(&self, object: &JObject) -> Option<JClassID> {
		let fun = self.vtable().GetObjectClass?;
		JClassID::from_sys(unsafe { fun(self.as_sys(), object.as_sys()) })
	}
	pub(crate) fn is_same_object(&self, object1: &JObject, object2: &JObject) -> Option<bool> {
		let fun = self.vtable().IsSameObject?;
		if sys::JNI_TRUE == unsafe { fun(self.as_sys(), object1.as_sys(), object2.as_sys()) } {
			Some(true)
		} else {
			Some(false)
		}
	}
	pub(crate) fn is_instance_of(&self, object: &JObject, class: JClassID) -> Option<bool> {
		let fun = self.vtable().IsInstanceOf?;
		if sys::JNI_TRUE == unsafe { fun(self.as_sys(), object.as_sys(), class.as_sys()) } {
			Some(true)
		} else {
			Some(false)
		}
	}
	pub(crate) fn ref_type(&self, object: &JObject) -> JRefType {
		if let Some(fun) = self.vtable().GetObjectRefType {
			unsafe { fun(self.as_sys(), object.as_sys()) }
		} else {
			JRefType::JNIInvalidRefType
		}
	}

	pub fn stack_capacity(&self, capacity: JInt) -> Option<()> {
		let fun = self.vtable().EnsureLocalCapacity?;
		if sys::JNI_OK == unsafe { fun(self.as_sys(), capacity) } {
			Some(())
		} else {
			None
		}
	}
	pub fn scope<T: AsRef<JObject>>(&self, capacity: JInt, fun: impl Fn(&JEnv) -> &T) -> Option<&T> {
		let push = self.vtable().PushLocalFrame?;
		let pop = self.vtable().PopLocalFrame?;
		if sys::JNI_OK == unsafe { push(self.as_sys(), capacity) } {
			let ret = fun(self);
			let ret = unsafe { pop(self.as_sys(), ret.as_ref().as_sys()) };
			if !ret.is_null() {
				Some(unsafe { &*(ret as *mut T) })
			} else {
				None
			}
		} else {
			None
		}
	}
	pub fn push_frame<'a, T: AsRef<JObject>>(&'a self, capacity: JInt) -> Option<JFrame<'a, T>> {
		let push = self.vtable().PushLocalFrame?;
		if sys::JNI_OK == unsafe { push(self.as_sys(), capacity) } {
			Some(JFrame {
				env: self,
				poped: false,
				_t: PhantomData,
			})
		} else {
			None
		}
	}
	fn pop_frame<T: AsRef<JObject>>(&self, ret: Option<&T>) -> Option<&T> {
		let pop = self.vtable().PopLocalFrame?;
		if let Some(ret) = ret {
			let ret = unsafe { pop(self.as_sys(), ret.as_ref().as_sys()) };
			if !ret.is_null() {
				Some(unsafe { &*(ret as *mut T) })
			} else {
				None
			}
		} else {
			unsafe { pop(self.as_sys(), core::ptr::null_mut() as _) };
			None
		}
	}

	pub(crate) fn new_local<'a, 'b: 'a, T: AsRef<JObject> + 'static>(&'a self, object: &'b T) -> Option<JLocal<'a, T>> {
		let fun = self.vtable().NewLocalRef?;
		let new_ref = unsafe { fun(self.as_sys(), object.as_ref().as_sys()) };
		JLocal::try_from(new_ref).ok()
	}
	pub(crate) fn del_local(&self, object: &JObject) {
		if let Some(fun) = self.vtable().DeleteLocalRef {
			unsafe { fun(self.as_sys(), object.as_sys()) };
		}
	}
	pub(crate) fn new_global<T: AsRef<JObject>>(&self, object: &T) -> Option<JRc<T>> {
		let fun = self.vtable().NewGlobalRef?;
		let new_ref = unsafe { fun(self.as_sys(), object.as_ref().as_sys()) };
		JRc::try_from(new_ref).ok()
	}
	pub(crate) fn del_global(&self, object: &JObject) {
		if let Some(fun) = self.vtable().DeleteGlobalRef {
			unsafe { fun(self.as_sys(), object.as_sys()) };
		}
	}
	pub(crate) fn new_weak<T: AsRef<JObject>>(&self, object: &T) -> Option<JWeak<T>> {
		let fun = self.vtable().NewWeakGlobalRef?;
		let new_ref = unsafe { fun(self.as_sys(), object.as_ref().as_sys()) };
		JWeak::try_from(new_ref).ok()
	}
	pub(crate) fn del_weak(&self, object: &JObject) {
		if let Some(fun) = self.vtable().DeleteWeakGlobalRef {
			unsafe { fun(self.as_sys(), object.as_sys()) };
		}
	}

	pub(crate) fn method(&self, cls: JClassID, name: &[u8], sign: &[u8]) -> Option<JMethodID> {
		let fun = self.vtable().GetMethodID?;
		JMethodID::from_sys(unsafe {
			fun(
				self.as_sys(),
				cls.as_sys(),
				name.as_ptr() as *const ffi::c_char,
				sign.as_ptr() as *const ffi::c_char,
			)
		})
	}
	///
	/// Methods from these three families of operations are used to call a Java instance method from a native method.
	/// They only differ in their mechanism for passing parameters to the methods that they call.
	/// These families of operations invoke an instance (nonstatic) method on a Java object, according to the specified method ID.
	/// The methodID argument must be obtained by calling GetMethodID().
	///
	/// When these functions are used to call private methods and constructors,
	/// the method ID must be derived from the real class of obj, not from one of its superclasses.
	///
	pub(crate) fn call_virtual<const N: usize, T: JReturnMaker>(
		&self,
		target: &JObject,
		method: JMethodID,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		use JReturnType::*;
		#[allow(dangling_pointers_from_temporaries)]
		let args = args.as_jvalue_array().as_ptr();
		let ret = match T::RETURN_TYPE {
			Void => {
				let fun = self.vtable().CallVoidMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Bool => {
				let fun = self.vtable().CallBooleanMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Byte => {
				let fun = self.vtable().CallByteMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Char => {
				let fun = self.vtable().CallCharMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Short => {
				let fun = self.vtable().CallShortMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Int => {
				let fun = self.vtable().CallIntMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Long => {
				let fun = self.vtable().CallLongMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Float => {
				let fun = self.vtable().CallFloatMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Double => {
				let fun = self.vtable().CallDoubleMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Object => {
				let fun = self.vtable().CallObjectMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
		}?;
		unsafe { T::from_jvalue(ret) }
	}
	///
	/// These families of operations invoke an instance (nonstatic) method on a Java object, according to the specified class and method ID.
	/// The methodID argument must be obtained by calling GetMethodID() on the class clazz.
	///
	/// The CallNonvirtual<type>Method families of routines and the Call<type>Method families of routines are different.
	/// Call<type>Method routines invoke the method based on the class or interface of the object,
	/// while CallNonvirtual<type>Method routines invoke the method based on the class, designated by the clazz parameter,
	/// from which the method ID is obtained.
	///
	/// The method ID must be obtained from the real class of the object or from one of its supertypes.
	///
	/// CallNonvirtual<type>Method routines are the mechanism for invoking “default interface methods” introduced in Java 8.
	///
	pub(crate) fn call_non_virtual<const N: usize, T: JReturnMaker>(
		&self,
		target: &JObject,
		class: JClassID,
		method: JMethodID,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		use JReturnType::*;
		#[allow(dangling_pointers_from_temporaries)]
		let args = args.as_jvalue_array().as_ptr();
		let ret = match T::RETURN_TYPE {
			Void => {
				let fun = self.vtable().CallNonvirtualVoidMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Bool => {
				let fun = self.vtable().CallNonvirtualBooleanMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Byte => {
				let fun = self.vtable().CallNonvirtualByteMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Char => {
				let fun = self.vtable().CallNonvirtualCharMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Short => {
				let fun = self.vtable().CallNonvirtualShortMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Int => {
				let fun = self.vtable().CallNonvirtualIntMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Long => {
				let fun = self.vtable().CallNonvirtualLongMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Float => {
				let fun = self.vtable().CallNonvirtualFloatMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Double => {
				let fun = self.vtable().CallNonvirtualDoubleMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Object => {
				let fun = self.vtable().CallNonvirtualObjectMethodA?;
				let ret = unsafe { fun(self.as_sys(), target.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
		}?;
		unsafe { T::from_jvalue(ret) }
	}

	pub(crate) fn field(&self, cls: JClassID, name: &[u8], sign: &[u8]) -> Option<JFieldID> {
		let fun = self.vtable().GetFieldID?;
		JFieldID::from_sys(unsafe {
			fun(
				self.as_sys(),
				cls.as_sys(),
				name.as_ptr() as *const ffi::c_char,
				sign.as_ptr() as *const ffi::c_char,
			)
		})
	}
	pub(crate) fn get_field<T: JReturnMaker>(&self, object: &JObject, field: JFieldID) -> Option<T> {
		use JReturnType::*;
		let ret = match T::RETURN_TYPE {
			Void => None,
			Bool => {
				let fun = self.vtable().GetBooleanField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Byte => {
				let fun = self.vtable().GetByteField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Char => {
				let fun = self.vtable().GetCharField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Short => {
				let fun = self.vtable().GetShortField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Int => {
				let fun = self.vtable().GetIntField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Long => {
				let fun = self.vtable().GetLongField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Float => {
				let fun = self.vtable().GetFloatField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Double => {
				let fun = self.vtable().GetDoubleField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Object => {
				let fun = self.vtable().GetObjectField?;
				let ret = unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
		}?;
		unsafe { T::from_jvalue(ret) }
	}
	pub(crate) fn set_field<T: JReturnMaker + AsJValue>(&self, object: &JObject, field: JFieldID, value: T) -> Option<()> {
		let value = value.as_jvalue();
		use JReturnType::*;
		match T::RETURN_TYPE {
			Void => (),
			Bool => {
				let fun = self.vtable().SetBooleanField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JBool::from_jvalue(value)?) };
				return Some(());
			}
			Byte => {
				let fun = self.vtable().SetByteField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JByte::from_jvalue(value)?) };
				return Some(());
			}
			Char => {
				let fun = self.vtable().SetCharField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JChar::from_jvalue(value)?) };
				return Some(());
			}
			Short => {
				let fun = self.vtable().SetShortField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JShort::from_jvalue(value)?) };
				return Some(());
			}
			Int => {
				let fun = self.vtable().SetIntField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JInt::from_jvalue(value)?) };
				return Some(());
			}
			Long => {
				let fun = self.vtable().SetLongField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JLong::from_jvalue(value)?) };
				return Some(());
			}
			Float => {
				let fun = self.vtable().SetFloatField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JFloat::from_jvalue(value)?) };
				return Some(());
			}
			Double => {
				let fun = self.vtable().SetDoubleField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), JDouble::from_jvalue(value)?) };
				return Some(());
			}
			Object => {
				let fun = self.vtable().SetObjectField?;
				unsafe { fun(self.as_sys(), object.as_sys(), field.as_sys(), sys::jobject::from_jvalue(value)?) };
				return Some(());
			}
		}
		None
	}

	pub(crate) fn method_static(&self, cls: JClassID, name: &[u8], sign: &[u8]) -> Option<JMethodStaticID> {
		let fun = self.vtable().GetStaticMethodID?;
		JMethodStaticID::from_sys(unsafe {
			fun(
				self.as_sys(),
				cls.as_sys(),
				name.as_ptr() as *const ffi::c_char,
				sign.as_ptr() as *const ffi::c_char,
			)
		})
	}
	pub(crate) fn call_static<const N: usize, T: JReturnMaker>(
		&self,
		class: JClassID,
		method: JMethodStaticID,
		args: impl AsJValueArray<N>,
	) -> Option<T> {
		use JReturnType::*;
		#[allow(dangling_pointers_from_temporaries)]
		let args = args.as_jvalue_array().as_ptr();
		let ret = match T::RETURN_TYPE {
			Void => {
				let fun = self.vtable().CallStaticVoidMethodA?;
				let _ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				None
			}
			Bool => {
				let fun = self.vtable().CallStaticBooleanMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Byte => {
				let fun = self.vtable().CallStaticByteMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Char => {
				let fun = self.vtable().CallStaticCharMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Short => {
				let fun = self.vtable().CallStaticShortMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Int => {
				let fun = self.vtable().CallStaticIntMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Long => {
				let fun = self.vtable().CallStaticLongMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Float => {
				let fun = self.vtable().CallStaticFloatMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Double => {
				let fun = self.vtable().CallStaticDoubleMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
			Object => {
				let fun = self.vtable().CallStaticObjectMethodA?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), method.as_sys(), args) };
				Some(ret.as_jvalue())
			}
		}?;
		unsafe { T::from_jvalue(ret) }
	}

	pub(crate) fn field_static(&self, cls: JClassID, name: &[u8], sign: &[u8]) -> Option<JFieldStaticID> {
		let fun = self.vtable().GetStaticFieldID?;
		JFieldStaticID::from_sys(unsafe {
			fun(
				self.as_sys(),
				cls.as_sys(),
				name.as_ptr() as *const ffi::c_char,
				sign.as_ptr() as *const ffi::c_char,
			)
		})
	}
	pub(crate) fn get_static<T: JReturnMaker>(&self, class: JClassID, field: JFieldStaticID) -> Option<T> {
		use JReturnType::*;
		let ret = match T::RETURN_TYPE {
			Void => None,
			Bool => {
				let fun = self.vtable().GetStaticBooleanField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Byte => {
				let fun = self.vtable().GetStaticByteField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Char => {
				let fun = self.vtable().GetStaticCharField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Short => {
				let fun = self.vtable().GetStaticShortField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Int => {
				let fun = self.vtable().GetStaticIntField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Long => {
				let fun = self.vtable().GetStaticLongField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Float => {
				let fun = self.vtable().GetStaticFloatField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Double => {
				let fun = self.vtable().GetStaticDoubleField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
			Object => {
				let fun = self.vtable().GetStaticObjectField?;
				let ret = unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys()) };
				Some(ret.as_jvalue())
			}
		}?;
		unsafe { T::from_jvalue(ret) }
	}
	pub(crate) fn set_static<T: JReturnMaker + AsJValue>(&self, class: JClassID, field: JFieldStaticID, value: T) -> Option<()> {
		let value = value.as_jvalue();
		use JReturnType::*;
		match T::RETURN_TYPE {
			Void => (),
			Bool => {
				let fun = self.vtable().SetStaticBooleanField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JBool::from_jvalue(value)?) };
				return Some(());
			}
			Byte => {
				let fun = self.vtable().SetStaticByteField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JByte::from_jvalue(value)?) };
				return Some(());
			}
			Char => {
				let fun = self.vtable().SetStaticCharField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JChar::from_jvalue(value)?) };
				return Some(());
			}
			Short => {
				let fun = self.vtable().SetStaticShortField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JShort::from_jvalue(value)?) };
				return Some(());
			}
			Int => {
				let fun = self.vtable().SetStaticIntField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JInt::from_jvalue(value)?) };
				return Some(());
			}
			Long => {
				let fun = self.vtable().SetStaticLongField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JLong::from_jvalue(value)?) };
				return Some(());
			}
			Float => {
				let fun = self.vtable().SetStaticFloatField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JFloat::from_jvalue(value)?) };
				return Some(());
			}
			Double => {
				let fun = self.vtable().SetStaticDoubleField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), JDouble::from_jvalue(value)?) };
				return Some(());
			}
			Object => {
				let fun = self.vtable().SetStaticObjectField?;
				unsafe { fun(self.as_sys(), class.as_sys(), field.as_sys(), sys::jobject::from_jvalue(value)?) };
				return Some(());
			}
		}
		None
	}
	pub fn register_natives(&self, class: JClassID, methods: &[JNativeMethod]) -> Result<(), JInt> {
		let fun = self.vtable().RegisterNatives.ok_or(0)?;
		let ret = unsafe { fun(self.as_sys(), class.as_sys(), methods.as_ptr(), methods.len() as _) };
		if ret == 0 {
			Ok(())
		} else {
			Err(ret)
		}
	}
	pub fn unregister_natives(&self, class: JClassID) -> Option<()> {
		let fun = self.vtable().UnregisterNatives?;
		let ret = unsafe { fun(self.as_sys(), class.as_sys()) };
		if ret == 0 {
			Some(())
		} else {
			None
		}
	}
	pub const fn native(name: &[u8], sign: &[u8], fun: *const ffi::c_void) -> JNativeMethod {
		JNINativeMethod {
			name: name.as_ptr() as *const ffi::c_char as _,
			signature: sign.as_ptr() as *const ffi::c_char as _,
			fnPtr: fun as _,
		}
	}
}
pub struct JFrame<'a, T: AsRef<JObject>> {
	env: &'a JEnv,
	poped: bool,
	_t: PhantomData<T>,
}
impl<'a, T: AsRef<JObject>> Drop for JFrame<'a, T> {
	fn drop(&mut self) {
		if !self.poped {
			self.env.pop_frame::<T>(None);
		}
	}
}
impl<'a, T: AsRef<JObject>> JFrame<'a, T> {
	pub fn pop(&mut self, result: &T) -> Option<&T> {
		self.poped = true;
		self.env.pop_frame(Some(result))
	}
}
